package com.cmcc.awareness;

import jpcap.packet.TCPPacket;
import jpcap.packet.UDPPacket;
import org.apache.http.HttpRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pcap4j.packet.TcpPacket;
import org.pcap4j.packet.UdpPacket;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName AppDistinguish
 * @Description TODO
 * @Author wangsiqi
 * @Date 2021/2/10 9:21
 * @Version 1.0
 * @Discription
 **/

public class AppDistinguish {
    private static Logger logger = LogManager.getLogger(AppDistinguish.class);

    /**
     * http业务识别
     * @param appDistinguishPath 文件路径下一个proto.list文件，多个http.proto和proto.dat文件
     */
    public Map<String,String> httpAppDistinguish (HttpRequest request,String appDistinguishPath) {
        Map<String,String> appResult = new HashMap<String,String>();
        String protolistPath = String.valueOf(getAppDistinguishPaths(appDistinguishPath).get("protolistPath"));
        List<String> httpprotoPathList = (List<String>) getAppDistinguishPaths(appDistinguishPath).get("httpprotoList");

        HttpProto httpProtoPool = getHttpProtoPool (httpprotoPathList);

        AppInfo appInfo = httpProtoPool.appDistinguish(request,protolistPath);

        if(appInfo!=null){
            //填写APP_type
            appResult.put("appType",appInfo.getNum_type());
            //填写APP_SUB_type
            appResult.put("appSubType",appInfo.getNum_subtype());
            return appResult;
        }else{
//            HttpProtoDistinguishStatistics.getInstance().incrementUnDistinguish();
            logger.debug("\n[ do not match any app rule ]");
            return null;
        }

    }
    
    public Map<String,String> httpAppDistinguishPcap4j (HttpRequest request,String protolistPath,List<String> httpprotoPathList) {
        Map<String,String> appResult = new HashMap<String,String>();

        HttpProto httpProtoPool = getHttpProtoPool (httpprotoPathList);
        AppInfo appInfo = httpProtoPool.appDistinguish(request,protolistPath);

        if(appInfo!=null){
            //填写APP_type
            appResult.put("appType",appInfo.getNum_type());
            //填写APP_SUB_type
            appResult.put("appSubType",appInfo.getNum_subtype());
            //填写APP_Content
            appResult.put("appContent",appInfo.getNum_content());
            return appResult;
        }else{
//            HttpProtoDistinguishStatistics.getInstance().incrementUnDistinguish();
            logger.debug("\n[ do not match any app rule ]");
            return null;
        }

    }
    
    public Map<String,Object> httpAppDistinguishWithRulePcap4j (HttpRequest request,String protolistPath,List<String> httpprotoPathList) {
        Map<String,Object> appResult = new HashMap<String,Object>();

        HttpProto httpProtoPool = getHttpProtoPool (httpprotoPathList);
        Map<String,Object> result = httpProtoPool.appDistinguishWithRule(request,protolistPath);

        if(result!=null&&result.get("appInfo")!=null){
        	AppInfo appInfo = (AppInfo)result.get("appInfo");
            //填写APP_type
            appResult.put("appType",appInfo.getNum_type());
            //填写APP_SUB_type
            appResult.put("appSubType",appInfo.getNum_subtype());
            //填写APP_Content
            appResult.put("appContent",appInfo.getNum_content());
            //返回命中的规则
            appResult.put("rule", result.get("rule"));
            return appResult;
        }else{
//            HttpProtoDistinguishStatistics.getInstance().incrementUnDistinguish();
            logger.debug("\n[ do not match any app rule ]");
            return null;
        }

    }

    /**
     * https业务识别
     * @param appDistinguishPath 文件路径下一个proto.list文件，多个http.proto和proto.dat文件
     */
    public Map<String,String> httpsAppDistinguish (String server_name_final, String id_at_commonName_final,String appDistinguishPath) {
        Map<String,String> appResult = new HashMap<String,String>();
        String protolistPath = String.valueOf(getAppDistinguishPaths(appDistinguishPath).get("protolistPath"));
        List<String> httpprotoPathList = (List<String>) getAppDistinguishPaths(appDistinguishPath).get("httpprotoList");

        HttpProto httpProtoPool = getHttpProtoPool (httpprotoPathList);
        AppInfo appInfo = httpProtoPool.appDistinguish(server_name_final, id_at_commonName_final, protolistPath);
        if(appInfo!=null){
            //填写APP_type
            appResult.put("appType",appInfo.getNum_type());
            //填写APP_SUB_type
            appResult.put("appSubType",appInfo.getNum_subtype());
            return appResult;

        }else{
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }

    /**
     * tco业务识别
     * @param appDistinguishPath 文件路径下一个proto.list文件，多个http.proto和proto.dat文件
     */
    public Map<String,String> tcpAppDistinguish (ArrayList<TCPPacket> altemp,String protolistPath,List<String> protodatList) {
        Map<String,String> appResult = new HashMap<String,String>();
        
        ProtoDat protodPool = getProtoDatPool (protodatList);

        for (String protodatPath : protodatList) {
            protodPool.protoDatApps.addAll(new ProtoDat(protodatPath).protoDatApps);
        }

        AppInfo appInfo = null;
        /*    以下调用业务识别模块       */
        for(TCPPacket tp:altemp){
            appInfo = protodPool.appDistinguish(tp,protolistPath);
            if(appInfo!=null){
                logger.debug("\n[ match app rule ]"+appInfo.toString());
                //填写APP_type
                appResult.put("appType",appInfo.getNum_type());
                //填写APP_SUB_type
                appResult.put("appSubType",appInfo.getNum_subtype());
                break;
            }
        }
        if(appInfo == null){
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }
    
    public Map<String,String> tcpAppDistinguishPcap4j (ArrayList<TcpPacket> altemp,String protolistPath,List<String> protodatList) {
        Map<String,String> appResult = new HashMap<String,String>();
        ProtoDat protodPool = getProtoDatPool (protodatList);

        for (String protodatPath : protodatList) {
            protodPool.protoDatApps.addAll(new ProtoDat(protodatPath).protoDatApps);
        }

        AppInfo appInfo = null;
        /*    以下调用业务识别模块       */
        for(TcpPacket tp:altemp){
            appInfo = protodPool.appDistinguish(tp,protolistPath);
            if(appInfo!=null){
                logger.debug("\n[ match app rule ]"+appInfo.toString());
                //填写APP_type
                appResult.put("appType",appInfo.getNum_type());
                //填写APP_SUB_type
                appResult.put("appSubType",appInfo.getNum_subtype());
                //填写APP_Content
                appResult.put("appContent",appInfo.getNum_content());
                break;
            }
        }
        if(appInfo == null){
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }
    
    public Map<String,Object> tcpAppDistinguishWithRulePcap4j (ArrayList<TcpPacket> altemp,String protolistPath,List<String> protodatList) {
        Map<String,Object> appResult = new HashMap<String,Object>();
        ProtoDat protodPool = getProtoDatPool (protodatList);

        for (String protodatPath : protodatList) {
            protodPool.protoDatApps.addAll(new ProtoDat(protodatPath).protoDatApps);
        }

        AppInfo appInfo = null;
        /*    以下调用业务识别模块       */
        for(TcpPacket tp:altemp){
        	Map<String,Object> result = protodPool.appDistinguishWithRule(tp,protolistPath);
            if(result!=null&&result.get("appInfo")!=null){
                logger.debug("\n[ match app rule ]"+appInfo.toString());
                appInfo = (AppInfo)result.get("appInfo");
                //填写APP_type
                appResult.put("appType",appInfo.getNum_type());
                //填写APP_SUB_type
                appResult.put("appSubType",appInfo.getNum_subtype());
                //填写APP_Content
                appResult.put("appContent",appInfo.getNum_content());
                //命中的规则
                appResult.put("rule", result.get("rule"));
                break;
            }
        }
        if(appInfo == null){
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }

    /**
     * https业务识别
     * @param appDistinguishPath 文件路径下一个proto.list文件，多个http.proto和proto.dat文件
     */
    public Map<String,String> httpsAppDistinguishPcap4j (String server_name_final, String id_at_commonName_final,String protolistPath,List<String> httpprotoPathList) {
        Map<String,String> appResult = new HashMap<String,String>();

        HttpProto httpProtoPool = getHttpProtoPool (httpprotoPathList);
        AppInfo appInfo = httpProtoPool.appDistinguish(server_name_final, id_at_commonName_final, protolistPath);
        if(appInfo!=null){
            //填写APP_type
            appResult.put("appType",appInfo.getNum_type());
            //填写APP_SUB_type
            appResult.put("appSubType",appInfo.getNum_subtype());
            //填写APP_Content
            appResult.put("appContent",appInfo.getNum_content());
            return appResult;

        }else{
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }
    
    public Map<String,Object> httpsAppDistinguishWithRulePcap4j (String server_name_final, String id_at_commonName_final,String protolistPath,List<String> httpprotoPathList) {
        Map<String,Object> appResult = new HashMap<String,Object>();

        HttpProto httpProtoPool = getHttpProtoPool (httpprotoPathList);
        Map<String,Object> result = httpProtoPool.appDistinguishWithRule(server_name_final, id_at_commonName_final, protolistPath);
        if(result!=null&&result.get("appInfo")!=null){
        	AppInfo appInfo = (AppInfo)result.get("appInfo");
            //填写APP_type
            appResult.put("appType",appInfo.getNum_type());
            //填写APP_SUB_type
            appResult.put("appSubType",appInfo.getNum_subtype());
            //填写APP_Content
            appResult.put("appContent",appInfo.getNum_content());
            //返回命中的规则
            appResult.put("rule", result.get("rule"));
            return appResult;

        }else{
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }
    
    /**
     * udp业务识别
     * @param appDistinguishPath 文件路径下一个proto.list文件，多个http.proto和proto.dat文件
     */
    public Map<String,String> udpAppDistinguish (ArrayList<UDPPacket> altemp,String protolistPath,List<String> protodatList) {
        ProtoDat protodPool = new ProtoDat(protodatList.get(0));
        Map<String,String> appResult = new HashMap<String,String>();
        for (String protodatPath : protodatList) {
            protodPool.protoDatApps.addAll(new ProtoDat(protodatPath).protoDatApps);
        }

        AppInfo appInfo = null;
        /*    以下调用业务识别模块       */
        for(UDPPacket up:altemp){
            appInfo = protodPool.appDistinguish(up,protolistPath);
            if(appInfo!=null){
                logger.debug("\n[ match app rule ]"+appInfo.toString());
                //填写APP_type
                appResult.put("appType",appInfo.getNum_type());
                //填写APP_SUB_type
                appResult.put("appSubType",appInfo.getNum_subtype());
                break;
            }
        }
        if(appInfo == null){
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }

    public Map<String,Object> udpAppDistinguishWithRulePcap4j (ArrayList<UdpPacket> altemp,String protolistPath,List<String> protodatList) {
        ProtoDat protodPool = new ProtoDat(protodatList.get(0));
        Map<String,Object> appResult = new HashMap<String,Object>();
        for (String protodatPath : protodatList) {
            protodPool.protoDatApps.addAll(new ProtoDat(protodatPath).protoDatApps);
        }

        AppInfo appInfo = null;
        /*    以下调用业务识别模块       */
        for(UdpPacket up:altemp){
        	Map<String,Object> result = protodPool.appDistinguishWithRule(up,protolistPath);
            if(appInfo!=null&&result.get("appInfo")!=null){
            	appInfo = (AppInfo)result.get("appInfo");
                logger.debug("\n[ match app rule ]"+appInfo.toString());
                //填写APP_type
                appResult.put("appType",appInfo.getNum_type());
                //填写APP_SUB_type
                appResult.put("appSubType",appInfo.getNum_subtype());
                //填写APP_Content
                appResult.put("appContent",appInfo.getNum_content());
                //返回命中的规则
                appResult.put("rule", result.get("rule"));
                break;
            }
        }
        if(appInfo == null){
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }
    
    public Map<String,String> udpAppDistinguishPcap4j (ArrayList<UdpPacket> altemp,String protolistPath,List<String> protodatList) {
        ProtoDat protodPool = new ProtoDat(protodatList.get(0));
        Map<String,String> appResult = new HashMap<String,String>();
        for (String protodatPath : protodatList) {
            protodPool.protoDatApps.addAll(new ProtoDat(protodatPath).protoDatApps);
        }

        AppInfo appInfo = null;
        /*    以下调用业务识别模块       */
        for(UdpPacket up:altemp){
            appInfo = protodPool.appDistinguish(up,protolistPath);
            if(appInfo!=null){
                logger.debug("\n[ match app rule ]"+appInfo.toString());
                //填写APP_type
                appResult.put("appType",appInfo.getNum_type());
                //填写APP_SUB_type
                appResult.put("appSubType",appInfo.getNum_subtype());
                //填写APP_Content
                appResult.put("appContent",appInfo.getNum_content());
                break;
            }
        }
        if(appInfo == null){
            logger.debug("\n[ do not match any app rule ]");
        }
        return appResult;
    }

    /**
     * 获取文件夹下不同类型文件的路径
     * @param appDistinguishPath 文件路径下一个proto.list文件，多个http.proto和proto.dat文件
     */
    public Map<String,Object> getAppDistinguishPaths(String appDistinguishPath){
        Map<String,Object> appDistinguishPaths = new HashMap<String,Object>();
        appDistinguishPaths.put("protolistPath",null);
        List<String> httpprotoList = new ArrayList<String>();
        List<String> protodatList = new ArrayList<String>();
        File fileFolder = new File(appDistinguishPath);
        File[] files = fileFolder.listFiles();
        if (files != null) {
            logger.info("[AppDistinguish] 扫描目录 ： " + fileFolder.getAbsolutePath() + "，  文件数 ： " + files.length);
            for (File file : files) {
                if (file.getName().endsWith("list")) {
                    appDistinguishPaths.put("protolistPath",file.getAbsolutePath());
                }else if(file.getName().endsWith("proto")) {
                    httpprotoList.add(file.getAbsolutePath());
                }else if(file.getName().endsWith("dat")) {
                    protodatList.add(file.getAbsolutePath());
                }
            }
            appDistinguishPaths.put("httpprotoList",httpprotoList);
            appDistinguishPaths.put("protodatList",protodatList);

        }
        return  appDistinguishPaths;
    }
    
    /**
     * 获取文件夹下不同类型文件的路径
     * @param appDistinguishPath 文件路径下一个proto.list文件，多个http.proto和proto.dat文件
     */
    public Map<String,Object> getAppDistinguishPaths(String appDistinguishPath,String protolistPath,List<String> httpprotoList,List<String> protodatList){
        Map<String,Object> appDistinguishPaths = new HashMap<String,Object>();
    	appDistinguishPaths.put("protolistPath", protolistPath);
        appDistinguishPaths.put("httpprotoList",httpprotoList);
        appDistinguishPaths.put("protodatList",protodatList);
        return  appDistinguishPaths;
    }

    /**
     * 把多个http.proto文件整合成一个HttpProto对象
     *
     */
    public HttpProto getHttpProtoPool (List<String> httpprotoPathList) {
        if (httpprotoPathList.size()>0) {
            HttpProto httpProtoPool = new HttpProto(httpprotoPathList.get(0));
            for (int i=1; i<httpprotoPathList.size(); i++) {
                    HttpProto httpProto = new HttpProto(httpprotoPathList.get(i));
                    //优先级0
                    HashMap<String,HttpProtoApp> priority0Map = httpProto.httpProtoApps_priority0;
                    if(priority0Map!=null) {
                    	priority0Map.forEach((k,v)->{
                    		if(httpProtoPool.httpProtoApps_priority0.containsKey(k)) {
                    			httpProtoPool.httpProtoApps_priority0.get(k).rules.addAll(v.rules);
                    		}else {
                    			httpProtoPool.httpProtoApps_priority0.put(k, v);
                    		}
                    	});
                    }
                    //优先级1
                    HashMap<String,HttpProtoApp> priority1Map = httpProto.httpProtoApps_priority1;
                    if(priority1Map!=null) {
                    	priority1Map.forEach((k,v)->{
                    		if(httpProtoPool.httpProtoApps_priority1.containsKey(k)) {
                    			httpProtoPool.httpProtoApps_priority1.get(k).rules.addAll(v.rules);
                    		}else {
                    			httpProtoPool.httpProtoApps_priority1.put(k, v);
                    		}
                    	});
                    }
                    //优先级2
                    HashMap<String,HttpProtoApp> priority2Map = httpProto.httpProtoApps_priority2;
                    if(priority2Map!=null) {
                    	priority2Map.forEach((k,v)->{
                    		if(httpProtoPool.httpProtoApps_priority2.containsKey(k)) {
                    			httpProtoPool.httpProtoApps_priority2.get(k).rules.addAll(v.rules);
                    		}else {
                    			httpProtoPool.httpProtoApps_priority2.put(k, v);
                    		}
                    	});
                    }
//                    
//                    httpProtoPool.httpProtoApps_priority0.putAll(httpProto.httpProtoApps_priority0);
//                    httpProtoPool.httpProtoApps_priority1.putAll(httpProto.httpProtoApps_priority1);
//                    httpProtoPool.httpProtoApps_priority2.putAll(httpProto.httpProtoApps_priority2);
            }
            return httpProtoPool;
        }else {
            logger.error("没有http.proto文件！");
            return null;
        }
    }

    /**
     * 把多个proto.dat文件整合成一个ProtoDat对象
     *
     */
    public ProtoDat getProtoDatPool (List<String> protodatPathList) {
        if (protodatPathList.size()>0) {
            ProtoDat protodPool = new ProtoDat(protodatPathList.get(0));
            for (int i=1; i<protodatPathList.size(); i++) {
                protodPool.protoDatApps.addAll(new ProtoDat(protodatPathList.get(i)).protoDatApps);
            }
            return protodPool;
        }else {
            logger.error("没有找到proto.dat文件！");
            return null;
        }
    }

    /**
     * 测试类，可扩展
     *
     */
    public static void main(String[] args) {
//        HttpRequest hq;
//        String test_path = "\\D:\\appfile";
//
//        AppDistinguish adg = new AppDistinguish();
//        System.out.println(adg.httpsAppDistinguish("aa.tudou.com","",test_path));
    	AppDistinguish a = new AppDistinguish();
    	List<String> list = new ArrayList<>();
    	list.add("D:\\bug\\http.proto");
    	list.add("D:\\bug\\douyin.proto");
    	
    	HttpProto protodPool = a.getHttpProtoPool (list);
    	protodPool.httpProtoApps_priority0.get("douyin");
    	protodPool.httpProtoApps_priority1.get("douyin");
    	protodPool.httpProtoApps_priority2.get("douyin");
    	System.out.print(protodPool);
    	
  

    }




}
